#include <stdio.h>

#include "Automation.h"
#include "Queue.h"
#include "Huffman.h"

#define ERROR 100

/* The length of each wave should be similar, because the length of normalized wave is the longest one. 
*/
Wave* Normalization(Wave* w)
{
	int i, nWaves = 0, lenWave = 0;
	int start = 0, length = 0, n0x80 = 0;
	BOOL walking = FALSE;
	Wave *wo;
	Queue queue;
	Queue_Init(&queue);

	// analysis
	for(i = 0,nWaves = 0, lenWave = 0; i < w->nChannels * w->nSamples * w->bps / 8; i++)
	{
		if (w->samples[i] == (BYTE)0x80)
		{
			if (walking == TRUE)
			{
				n0x80++;
				if (n0x80 > ERROR) // other values might appear in 30 bytes
				{
					Queue_Add(&queue, start, i - start - ERROR);
					nWaves++;
					lenWave = lenWave > i - start - ERROR ? lenWave : i - start - ERROR;
					n0x80 = 0;
					walking = FALSE;
				}
			}
		}
		else /* w->samples[i] != 0x80 */
		{
			if (walking == FALSE)
			{
				walking = TRUE;
				start = i;
			}
			n0x80 = 0;
		}
	}

	// generate new wave
	wo = (Wave*)malloc(sizeof(Wave));
	wo->samples = (BYTE*)malloc(nWaves * lenWave * sizeof(BYTE));
	memset(wo->samples, 0x80, nWaves * lenWave * sizeof(BYTE));
	for(i = 0; i < nWaves; i++)
	{
		Queue_Remove(&queue, &start, &length);
		if (length < lenWave / 3) // filter error(s)
		{
			nWaves--;
			i--;
			continue;
		}
		memcpy(&wo->samples[i * lenWave], &w->samples[start], sizeof(BYTE) * length);
	}
	
	wo->samples = (BYTE*)realloc(wo->samples, nWaves * lenWave * sizeof(BYTE));
	wo->nChannels = w->nChannels;
	wo->nSamples = (nWaves * lenWave * sizeof(BYTE) * 8) / (w->nChannels * w->bps);
	wo->samplerates = w->samplerates;
	wo->bps = w->bps;
	wo->lenWave = lenWave;
	wo->nWaves = nWaves;

	return wo;
}

/** Least Huffman Series ( .lhuffs )
	-----------------------
	|  Header  | Wave     |
	|          | Entries  |
	-----------------------
	| Encode Size | Data1 |
	--------------|       |
	| ...                 |
	-----------------------
	| Encode Size | Data2 |
	--------------|       |
	| ...                 |
	-----------------------
	| ...                 |
	-----------------------
**/
Wave* Encode_LeastHuffmanSeries(Wave* norm_w)
{
	int i, desLen, totDesLen, nHuffLen, nNodeSize;
	void * pHuffTree; 
	BYTE* pDes;
	Wave* wo;
	
	// encode waves
	wo->entries = (WaveEntry**)malloc(sizeof(WaveEntry*) * norm_w->nWaves);
	wo->samples = (BYTE*)malloc(norm_w->nWaves * norm_w->lenWave * sizeof(BYTE));
	pDes = wo->samples;
	pHuffTree = GenerateHuffmanTree(norm_w->samples, norm_w->nWaves * norm_w->lenWave * sizeof(BYTE), pDes, &nHuffLen);
	totDesLen = pDes - wo->samples;

	for(i = 0; i < norm_w->nWaves; i++)
	{
		CompressHuffmanUsingTree(&norm_w->samples[i * norm_w->lenWave], norm_w->lenWave,
			&pDes, &desLen, pHuffTree);
		wo->entries[i] = (WaveEntry*)malloc(sizeof(WaveEntry)); // set wave_entries
		wo->entries[i]->index = totDesLen;
		wo->entries[i]->length = desLen;
		memcpy(&wo->samples[totDesLen], pDes, desLen); // write the encoded data
		totDesLen += desLen;
		free(pDes);
	}
	// construct header
	wo->samples = (BYTE*)realloc(wo->samples, totDesLen * sizeof(BYTE));
	wo->encoded = TRUE;
	wo->bps = norm_w->bps;
	wo->nChannels = norm_w->nChannels;
	wo->nSamples = norm_w->nSamples;
	wo->nWaves = norm_w->nWaves;
	wo->samplerates = norm_w->samplerates;
	wo->sizeEncoded = totDesLen * norm_w->bps / 8;

	free(pHuffTree);
	return wo;
}

Wave* Decode_LeastHuffmanSeries(Wave* lhuff_w)
{
	return NULL;
}

Wave* GetWave(const char* filename, int index) 
{
	FILE* fd;
	Wave* wo;
	WaveEntry entry;

	if ((fd = fopen(filename, "rb")) == 0) return 0;
	wo = (Wave*)malloc(sizeof(Wave));
	if (fread(wo, sizeof(Wave), 1, fd) != sizeof(Wave)) goto WRONG;
	fseek(fd, sizeof(WaveEntry) * index, SEEK_SET); // approach entries table
	if (fread(&entry, sizeof(WaveEntry), 1, fd) != sizeof(WaveEntry)) goto WRONG;
	fseek(fd, sizeof(BYTE) * entry.index + sizeof(WaveEntry) * wo->nWaves + sizeof(Wave), SEEK_SET); // approach data
	wo->samples = (BYTE*)malloc(sizeof(BYTE) * entry.length);
	if (fread(wo->samples, sizeof(BYTE), entry.length, fd) != entry.length * sizeof(BYTE)) goto WRONG2;

	// construct wave headers
	/* // preserved information
	wo->bps = wo->bps;
	wo->nChannels = wo->nChannels;
	wo->samplerates = wo->samplerates;*/
	wo->entries = 0;
	wo->lenWave = 0;
	wo->nWaves = 0;
	wo->sizeEncoded = 0;
	wo->encoded = TRUE;
	wo->nSamples = wo->bps / 8 * entry.length * sizeof(BYTE);
	
	fclose(fd);
	return wo;

WRONG2:
	free(wo->samples);
WRONG:
	fclose(fd);
	free(wo);
	return 0;
}

//void Free(WaveEntry** wave_entries, int nEntries)
//{
//	int i;
//	for(i = 0; i < nEntries; i++)	free(wave_entries[i]);
//	free(wave_entries);
//}